home *** CD-ROM | disk | FTP | other *** search
/ Apple Developer Connection 1998 Fall: Game Toolkit / Disc.iso / SDKs / Third Party SDKs / ATI RAVE SDK / Samples / QD3D Tests / BackGround (ATI) / BackGroundSupport.c < prev    next >
Encoding:
Text File  |  1998-06-09  |  19.3 KB  |  726 lines  |  [TEXT/CWIE]

  1. // Quickdraw 3D sample code
  2. //
  3. // This file illustrates how to set up a pixmap based draw context.
  4. // A metafile is read into and imaged in the pixmap, this pixmap is combined
  5. // with a pixmap containing a background, so that the 3d data is drawn over 
  6. // the background
  7. //
  8. // Nick Thompson, AppleLink: DEVSUPPORT (devsupport@applelink.apple.com)
  9. //
  10. // ©1994-5 Apple Computer Inc., All Rights Reserved
  11.  
  12.  
  13.  
  14. #include <Files.h>
  15. #include <QuickDraw.h>
  16. #include <QDOffScreen.h>
  17. #include <StandardFile.h>
  18. #include <Sound.h>
  19. #include "BackGroundSupport.h"
  20.  
  21. #include "QD3D.h"
  22. #include "QD3DDrawContext.h"
  23. #include "QD3DRenderer.h"
  24. #include "QD3DShader.h"
  25. #include "QD3DCamera.h"
  26. #include "QD3DLight.h"
  27. #include "QD3DGeometry.h"
  28. #include "QD3DGroup.h"
  29. #include "QD3DMath.h"
  30. #include "QD3DTransform.h"
  31. #include "QD3DStorage.h"
  32. #include "QD3DIO.h"
  33.  
  34. #undef kQ3RealZero            // temp chrisb
  35. #define kQ3RealZero 0.0
  36.  
  37. //-----------------------------------------------------------------------------------------------
  38. // local utility functions
  39. static    TQ3FileObject         MyGetNewFile( FSSpec *myFSSpec, TQ3Boolean *isText ) ;
  40.  
  41. static void GetGroupBBox(
  42.     DocumentPtr            theDocument,
  43.     TQ3BoundingBox         *viewBBox) ;
  44.                                                 
  45. static    TQ3Status MyAddShaderToGroup( TQ3GroupObject group ) ;
  46.  
  47. static TQ3Status GetDocumentGroupBoundingBox( 
  48.     DocumentPtr theDocument , 
  49.     TQ3BoundingBox *viewBBox) ;
  50.  
  51. //-----------------------------------------------------------------------------------------------
  52. // Submit the scene for rendering/fileIO and picking
  53. TQ3Status SubmitScene( DocumentPtr theDocument ) 
  54. {        
  55.     TQ3Vector3D                globalScale;
  56.     TQ3Vector3D                globalTranslate;
  57.     
  58.     globalScale.x = globalScale.y = globalScale.z = theDocument->fGroupScale;
  59.     globalTranslate = *(TQ3Vector3D *)&theDocument->fGroupCenter;
  60.     Q3Vector3D_Scale(&globalTranslate, -1, &globalTranslate);
  61.     Q3Style_Submit(theDocument->fInterpolation, theDocument->fView);
  62.     Q3Style_Submit(theDocument->fBackFacing , theDocument->fView);
  63.     Q3Style_Submit(theDocument->fFillStyle, theDocument->fView);
  64.         
  65.     Q3MatrixTransform_Submit( &theDocument->fRotation, theDocument->fView);
  66.         
  67.     Q3ScaleTransform_Submit(&globalScale, theDocument->fView);
  68.     Q3TranslateTransform_Submit(&globalTranslate, theDocument->fView);
  69.     Q3DisplayGroup_Submit( theDocument->fModel, theDocument->fView);
  70.     
  71.     return kQ3Success ;
  72. }
  73.  
  74. //-----------------------------------------------------------------------------------------------
  75.  
  76. static TQ3Status GetDocumentGroupBoundingBox( 
  77.     DocumentPtr theDocument , 
  78.     TQ3BoundingBox *viewBBox)
  79. {
  80.     TQ3Status        status;
  81.     TQ3ViewStatus    viewStatus ;
  82.     
  83.     status = Q3View_StartBoundingBox( theDocument->fView, kQ3ComputeBoundsApproximate );
  84.     do {
  85.         status = SubmitScene( theDocument ) ;
  86.     } while((viewStatus = Q3View_EndBoundingBox( theDocument->fView, viewBBox )) == kQ3ViewStatusRetraverse );
  87.     return status ;
  88. }
  89. //-----------------------------------------------------------------------------------------------
  90.  
  91. TQ3ViewObject MyNewView(GWorldPtr myOffscreenGWorld)
  92. {
  93.     TQ3Status                myStatus;
  94.     TQ3ViewObject            myView;
  95.     TQ3DrawContextObject        myDrawContext;
  96.     TQ3RendererObject        myRenderer;
  97.     TQ3CameraObject            myCamera;
  98.     TQ3GroupObject            myLights;
  99.     
  100.     if((myView = Q3View_New()) == NULL)
  101.         goto bail ;    
  102.         
  103.     //    Create and set draw context.
  104.     if ((myDrawContext = MyNewDrawContext(myOffscreenGWorld)) == NULL )
  105.         goto bail;
  106.         
  107.     if ((myStatus = Q3View_SetDrawContext(myView, myDrawContext)) == kQ3Failure )
  108.         goto bail;
  109.  
  110.     Q3Object_Dispose( myDrawContext ) ;
  111.     
  112.     //    Create and set renderer.
  113.     // this would use the interactive software renderer
  114.  
  115.     if ((myRenderer = Q3Renderer_NewFromType(kQ3RendererTypeInteractive)) != NULL ) {
  116.         if ((myStatus = Q3View_SetRenderer(myView, myRenderer)) == kQ3Failure ) {
  117.             goto bail;
  118.         }
  119.     }
  120.     else {
  121.         goto bail;
  122.     }
  123.  
  124.     Q3Object_Dispose( myRenderer ) ;
  125.     
  126.     //    Create and set camera.
  127.     if ( (myCamera = MyNewCamera(myOffscreenGWorld)) == NULL )
  128.         goto bail;
  129.         
  130.     if ((myStatus = Q3View_SetCamera(myView, myCamera)) == kQ3Failure )
  131.         goto bail;
  132.  
  133.     Q3Object_Dispose( myCamera ) ;
  134.     
  135.     //    Create and set lights.
  136.     if ((myLights = MyNewLights()) == NULL )
  137.         goto bail;
  138.         
  139.     if ((myStatus = Q3View_SetLightGroup(myView, myLights)) == kQ3Failure )
  140.         goto bail;
  141.         
  142.     Q3Object_Dispose(myLights);
  143.  
  144.     //    Done!!!
  145.     return ( myView );
  146.     
  147. bail:
  148.     //    If any of the above failed, then don't return a view.
  149.     SysBeep(10) ;
  150.     return ( NULL );
  151. }
  152.  
  153. //----------------------------------------------------------------------------------
  154.  
  155. TQ3DrawContextObject MyNewDrawContext(GWorldPtr theGWorld)
  156. {
  157.     TQ3PixmapDrawContextData    myDrawContextData;
  158.     TQ3ColorARGB                clearColor = {1.0, 0.0, 1.0, 0.0} ; 
  159.     PixMapHandle                 hPixMap ;
  160.     Rect                        srcRect ;
  161.  
  162.     float                        factor = 0xffff ;
  163.  
  164.     clearColor.r = kClearColor.red / factor ;
  165.     clearColor.g = kClearColor.green / factor ;
  166.     clearColor.b = kClearColor.blue / factor ;
  167.     
  168.     //    Fill in draw context data.
  169.     myDrawContextData.drawContextData.clearImageMethod = kQ3ClearMethodWithColor;
  170.     myDrawContextData.drawContextData.clearImageColor  = clearColor;
  171.  
  172.     myDrawContextData.drawContextData.paneState = kQ3False;
  173.     myDrawContextData.drawContextData.maskState = kQ3False;
  174.     
  175.     myDrawContextData.drawContextData.doubleBufferState = kQ3False;
  176.  
  177.     hPixMap = GetGWorldPixMap(theGWorld);
  178.     LockPixels(hPixMap);
  179.  
  180.     srcRect = theGWorld->portRect;
  181.  
  182.     myDrawContextData.pixmap.width     = srcRect.right  - srcRect.left;
  183.     myDrawContextData.pixmap.height    = srcRect.bottom - srcRect.top;
  184.     
  185.     myDrawContextData.pixmap.rowBytes = (**hPixMap).rowBytes & 0x7FFF;
  186.     
  187.     if( myDrawContextData.pixmap.rowBytes/myDrawContextData.pixmap.width == 4 )
  188.     {
  189.         myDrawContextData.pixmap.pixelType = kQ3PixelTypeRGB32;
  190.         myDrawContextData.pixmap.pixelSize = 32;
  191.     }
  192.     else
  193.     {
  194.         myDrawContextData.pixmap.pixelType = kQ3PixelTypeRGB16;
  195.         myDrawContextData.pixmap.pixelSize = 16;
  196.     }
  197.     
  198.     myDrawContextData.pixmap.bitOrder    = kQ3EndianBig;
  199.     myDrawContextData.pixmap.byteOrder    = kQ3EndianBig;
  200.     
  201.     myDrawContextData.pixmap.image        = GetPixBaseAddr( hPixMap );
  202.     
  203.     return Q3PixmapDrawContext_New(&myDrawContextData);
  204. }
  205. //----------------------------------------------------------------------------------
  206.  
  207. TQ3CameraObject MyNewCamera(CGrafPtr thePort)
  208. {
  209.     TQ3CameraObject                    myCamera;
  210.     TQ3CameraData                    myCameraData;
  211.     TQ3ViewAngleAspectCameraData        myViewAngleCameraData;
  212.     TQ3Point3D                        cameraFrom     = { 0.0, 0.0, 30.0 };
  213.     TQ3Point3D                        cameraTo     = { 0.0, 0.0, 0.0 };
  214.     TQ3Vector3D                        cameraUp     = { 0.0, 1.0, 0.0 };
  215.     
  216.     float                             fieldOfView = .52359333333;
  217.     float                             hither         = 0.001;
  218.     float                             yon         = 1000;
  219.     
  220.     //    Fill in camera data.
  221.     myCameraData.placement.cameraLocation = cameraFrom;
  222.     myCameraData.placement.pointOfInterest = cameraTo;
  223.     myCameraData.placement.upVector = cameraUp;
  224.     
  225.     myCameraData.range.hither = hither;
  226.     myCameraData.range.yon = yon;
  227.     
  228.     myCameraData.viewPort.origin.x = -1.0;
  229.     myCameraData.viewPort.origin.y = 1.0;
  230.     myCameraData.viewPort.width = 2.0;
  231.     myCameraData.viewPort.height = 2.0;
  232.     
  233.     myViewAngleCameraData.cameraData = myCameraData;
  234.     myViewAngleCameraData.fov = fieldOfView ;
  235.     
  236.     // set up the aspect ratio based on the window
  237.     myViewAngleCameraData.aspectRatioXToY =  
  238.             (float) (thePort->portRect.right - thePort->portRect.left) / 
  239.             (float) (thePort->portRect.bottom - thePort->portRect.top);
  240.  
  241.     myCamera = Q3ViewAngleAspectCamera_New(&myViewAngleCameraData);    
  242.     
  243.     //    Return the camera.
  244.     return ( myCamera );
  245. }
  246.  
  247.  
  248. //----------------------------------------------------------------------------------
  249.  
  250. TQ3GroupObject MyNewLights()
  251. {
  252.     TQ3GroupPosition            myGroupPosition;
  253.     TQ3GroupObject            myLightList;
  254.     TQ3LightData                myLightData;
  255.     TQ3PointLightData        myPointLightData;
  256.     TQ3DirectionalLightData    myDirectionalLightData;
  257.     TQ3LightObject            myAmbientLight, myPointLight, myFillLight;
  258.     TQ3Point3D                pointLocation = { -10.0, 0.0, 10.0 };
  259.     TQ3Vector3D                fillDirection = { 10.0, 0.0, 10.0 };
  260.     TQ3ColorRGB                WhiteLight = { 1.0, 1.0, 1.0 };
  261.     
  262.     //    Set up light data for ambient light.  This light data will be used for point and fill
  263.     //    light also.
  264.  
  265.     myLightData.isOn = kQ3True;
  266.     myLightData.color = WhiteLight;
  267.     
  268.     //    Create ambient light.
  269.     myLightData.brightness = .2;
  270.     myAmbientLight = Q3AmbientLight_New(&myLightData);
  271.     if ( myAmbientLight == NULL )
  272.         goto bail;
  273.     
  274.     //    Create point light.
  275.     myLightData.brightness = 1.0;
  276.     myPointLightData.lightData = myLightData;
  277.     myPointLightData.castsShadows = kQ3False;
  278.     myPointLightData.attenuation = kQ3AttenuationTypeNone;
  279.     myPointLightData.location = pointLocation;
  280.     myPointLight = Q3PointLight_New(&myPointLightData);
  281.     if ( myPointLight == NULL )
  282.         goto bail;
  283.  
  284.     //    Create fill light.
  285.     myLightData.brightness = .2;
  286.     myDirectionalLightData.lightData = myLightData;
  287.     myDirectionalLightData.castsShadows = kQ3False;
  288.     myDirectionalLightData.direction = fillDirection;
  289.     myFillLight = Q3DirectionalLight_New(&myDirectionalLightData);
  290.     if ( myFillLight == NULL )
  291.         goto bail;
  292.  
  293.     //    Create light group and add each of the lights into the group.
  294.     myLightList = Q3LightGroup_New();
  295.     if ( myLightList == NULL )
  296.         goto bail;
  297.     myGroupPosition = Q3Group_AddObject(myLightList, myAmbientLight);
  298.     if ( myGroupPosition == 0 )
  299.         goto bail;
  300.     myGroupPosition = Q3Group_AddObject(myLightList, myPointLight);
  301.     if ( myGroupPosition == 0 )
  302.         goto bail;
  303.     myGroupPosition = Q3Group_AddObject(myLightList, myFillLight);
  304.     if ( myGroupPosition == 0 )
  305.         goto bail;
  306.  
  307.     Q3Object_Dispose( myAmbientLight ) ;
  308.     Q3Object_Dispose( myPointLight ) ;
  309.     Q3Object_Dispose( myFillLight ) ;
  310.  
  311.     //    Done!
  312.     return ( myLightList );
  313.     
  314. bail:
  315.     //    If any of the above failed, then return nothing!
  316.     return ( NULL );
  317. }
  318.  
  319. //----------------------------------------------------------------------------------
  320.  
  321. TQ3GroupObject MyNewModelFromFile(FSSpec *theFileSpec)
  322. {
  323.     TQ3GroupObject        myGroup = NULL ;
  324.     TQ3Boolean            isText = kQ3False ;
  325.     TQ3FileMode            myFileMode ;    // we are reading the file
  326.     TQ3FileObject        theFile = kQ3FileModeNormal;
  327.     
  328.     //    Create a ordered group for the complete model.
  329.     if ((myGroup = Q3OrderedDisplayGroup_New()) == NULL )
  330.         return NULL;
  331.  
  332.     theFile = MyGetNewFile( theFileSpec, &isText ) ;
  333.     
  334.     if( isText == kQ3True )
  335.         myFileMode |= kQ3FileModeText;    // is it a text metafile??    
  336.  
  337.     // Open the file object
  338.     if( Q3File_OpenRead( theFile, &myFileMode ) != kQ3Success)
  339.         return  NULL ;
  340.  
  341.     MyReadModelFromFile( theFile, myGroup ) ;
  342.     
  343.     Q3File_Close(theFile);            // close and dispose of the file object
  344.     Q3Object_Dispose(theFile);
  345.     
  346.     MyAddShaderToGroup( myGroup ) ;
  347.     
  348.     return myGroup ;
  349. }
  350.  
  351.  
  352. //----------------------------------------------------------------------------------
  353. // attach a shader to the group
  354.  
  355. TQ3Status MyAddShaderToGroup( TQ3GroupObject group )
  356. {
  357.     TQ3ShaderObject    illuminationShader = Q3PhongIllumination_New();
  358.  
  359.     Q3Group_AddObject(group, illuminationShader);
  360.     Q3Object_Dispose(illuminationShader);
  361.     return(kQ3Success);
  362. }
  363.  
  364. //----------------------------------------------------------------------------------
  365. // read model from file object into the supplied group
  366.  
  367. TQ3Status MyReadModelFromFile( TQ3FileObject theFile,TQ3GroupObject myGroup)
  368. {    
  369.     if(theFile != NULL) {
  370.     
  371.         TQ3Object            myTempObj ;
  372.         TQ3Boolean            isEOF ;
  373.                 
  374.     
  375.         // read objects from the file
  376.         do {
  377.         
  378.             myTempObj = Q3File_ReadObject( theFile );
  379.             
  380.             if( myTempObj != NULL ) {
  381.                 // we only want the object in our main group if we can draw it
  382.                 if (Q3Object_IsDrawable( myTempObj) ) 
  383.                     Q3Group_AddObject( myGroup, myTempObj ) ;
  384.                 
  385.                 // we either added the object to the main group, or we don't care
  386.                 // so we can safely dispose of the object
  387.                 Q3Object_Dispose( myTempObj ) ;
  388.             }
  389.             
  390.             // check to see if we reached the end of file yet
  391.             isEOF = Q3File_IsEndOfFile( theFile );
  392.             
  393.         } while (isEOF == kQ3False);    
  394.     }
  395.     
  396.     if( myGroup != NULL )
  397.         return kQ3Success ;
  398.     else
  399.         return kQ3Failure ;
  400. }
  401.  
  402. //-----------------------------------------------------------------------------------------------
  403. // cleaned up from IM QuickDraw 3D pp 15-5
  404. static TQ3FileObject MyGetNewFile( FSSpec *myFSSpec, TQ3Boolean *isText )
  405. {
  406.     TQ3FileObject        myFileObj;
  407.     TQ3StorageObject        myStorageObj;
  408.     OSType                myFileType;
  409.     
  410.     FInfo                fndrInfo ;
  411.  
  412.     // we assume the FSSpec passed in was valid, get the file information
  413.     // we need to know the file type, this routine may get called by an appleEvent
  414.     // handler, so we can't assume a type, we need to get it from the fsspec.
  415.     
  416.     FSpGetFInfo( myFSSpec, &fndrInfo ) ;
  417.     
  418.     // pull out the file type
  419.     
  420.     myFileType = fndrInfo.fdType ;
  421.     
  422.     // Create new storage object and new file object 
  423.     if(((myStorageObj = Q3FSSpecStorage_New( myFSSpec )) == NULL) 
  424.         || ((myFileObj = Q3File_New()) == NULL)) 
  425.     {
  426.         if (myStorageObj != NULL) 
  427.             Q3Object_Dispose(myStorageObj);
  428.         return(NULL);
  429.     }
  430.  
  431.     // Set the storage for the file object
  432.     Q3File_SetStorage(myFileObj, myStorageObj);
  433.     Q3Object_Dispose(myStorageObj);
  434.  
  435.     if (myFileType == '3DMF')
  436.         *isText = kQ3False ;
  437.     else if (myFileType == 'TEXT')
  438.         *isText = kQ3True ;
  439.  
  440.     return (myFileObj);
  441. }
  442.  
  443.  
  444. //-------------------------------------------------------------------------------------------
  445. //
  446. Boolean MetafileFileSpecify( FSSpec *theFile )
  447. {
  448.     StandardFileReply    theSFReply ;
  449.     SFTypeList            myTypes = { '3DMF' } ;
  450.     const short            numTypes = 1 ;
  451.         
  452.     // Get the file name to open
  453.     StandardGetFile( nil, numTypes, myTypes, &theSFReply ) ;
  454.     
  455.     if( theSFReply.sfGood )
  456.         *theFile = theSFReply.sfFile ;
  457.     
  458.     // did the user cancel?
  459.     return theSFReply.sfGood ;
  460.     
  461. }
  462. //-------------------------------------------------------------------------------------------
  463. //
  464. Boolean PictureFileSpecify( FSSpec *theFile )
  465. {
  466.     StandardFileReply    theSFReply ;
  467.     SFTypeList            myTypes = { 'PICT' } ;
  468.     const short            numTypes = 1 ;
  469.         
  470.     // Get the file name to open
  471.     StandardGetFile( nil, numTypes, myTypes, &theSFReply ) ;
  472.     
  473.     if( theSFReply.sfGood )
  474.         *theFile = theSFReply.sfFile ;
  475.     
  476.     // did the user cancel?
  477.     return theSFReply.sfGood ;
  478.     
  479. }
  480.  
  481. //----------------------------------------------------------------------------------
  482. PicHandle OpenPICTFile( FSSpec *theFile )
  483. {
  484.     OSErr        err;
  485.     long        curEOF;
  486.     PicHandle    my_pic;
  487.     long         count;
  488.     Ptr         buffer;
  489.     short        refNum;
  490.     
  491.     if (FSpOpenDF(theFile, fsRdWrPerm, &refNum))
  492.         return(0);
  493.  
  494.     /* get size of file */
  495.     err = GetEOF(refNum, &curEOF);
  496.     if (err != 0) {
  497.         return(0);
  498.     }
  499.     
  500.     /* move the file mark to 512 */
  501.     err = SetFPos(refNum, fsFromStart, 512L);
  502.     if (err != 0) {
  503.         return(0);
  504.     }
  505.  
  506.     /* size of data to read */
  507.     count = curEOF - 512;
  508.     
  509.     /* create the PicHandle */
  510.     my_pic = (PicHandle)NewHandle(count);
  511.     HLock((Handle)my_pic);
  512.     
  513.     /* read the PICT info */
  514.     buffer = (Ptr)(*my_pic);
  515.     err = FSRead(refNum, &count, buffer);
  516.     if (err != 0) {
  517.         return(0);
  518.     }
  519.     HUnlock((Handle)my_pic);
  520.  
  521.     FSClose(refNum);
  522.     
  523.     return (my_pic);
  524. }
  525. //----------------------------------------------------------------------------------
  526.  
  527.  
  528. void GetGroupBBox(
  529.     DocumentPtr            theDocument,
  530.     TQ3BoundingBox         *viewBBox)
  531. {
  532.     TQ3Point3D                     from     = { 0.0, 0.0, 1.0 };
  533.     TQ3Point3D                     to         = { 0.0, 0.0, 0.0 };
  534.     TQ3Vector3D                     up         = { 0.0, 1.0, 0.0 };
  535.     
  536.     float                         fieldOfView = .52359333333;
  537.     float                         hither         =  0.5;
  538.     float                         yon         =  1.5;
  539.     TQ3GroupObject                mainGroup = theDocument->fModel ;
  540.  
  541.     TQ3Status                    status;
  542.     
  543. #ifdef BETA_1_BUILD
  544.     Q3View_StartBounds( theDocument->fView );
  545.  
  546.     status = Q3DisplayGroup_BoundingBox(mainGroup, 
  547.                                         viewBBox, 
  548.                                         kQ3ComputeBoundsApproximate,
  549.                                          viewObject);
  550.  
  551.     Q3View_EndBounds( theDocument->fView );
  552. #else
  553.     status = GetDocumentGroupBoundingBox( theDocument , viewBBox) ;
  554. #endif
  555.                                         
  556.     //
  557.     //  If we have a point model, then the "viewBBox" would end up
  558.     //  being a "singularity" at the location of the point.  As
  559.     //  this bounding "box" is used in setting up the camera spec,
  560.     //  we get bogus input into Escher.
  561.     
  562.     {
  563.          float        xSize, ySize, zSize;
  564.         
  565.         xSize = viewBBox->max.x - viewBBox->min.x;
  566.         ySize = viewBBox->max.y - viewBBox->min.y;
  567.         zSize = viewBBox->max.z - viewBBox->min.z;
  568.  
  569.         if (xSize <= kQ3RealZero &&
  570.             ySize <= kQ3RealZero &&
  571.             zSize <= kQ3RealZero) {
  572.             
  573.             viewBBox->max.x += 0.0001;
  574.             viewBBox->max.y += 0.0001;
  575.             viewBBox->max.z += 0.0001;
  576.             
  577.             viewBBox->min.x -= 0.0001;
  578.             viewBBox->min.y -= 0.0001;
  579.             viewBBox->min.z -= 0.0001;
  580.         }
  581.     }
  582. }
  583.  
  584.  
  585.  
  586.  
  587. //------------------------------------------------------------------------
  588.  
  589.  
  590. TQ3Point3D AdjustCamera(
  591.     DocumentPtr            theDocument,
  592.     short                winWidth,
  593.     short                winHeight)
  594. {
  595.     float                         fieldOfView;
  596.     float                         hither;
  597.     float                         yon;
  598.     TQ3CameraPlacement            placement;
  599.     TQ3CameraRange                range;
  600.     TQ3BoundingBox                 viewBBox;
  601.     long                         fromAxis;    
  602.     float                         maxDimension;
  603.      float                        xSize, ySize, zSize;
  604.     float                        weights[2] = { 0.5, 0.5 };
  605.     TQ3Point3D                    points[2];
  606.     TQ3Vector3D                     viewVector;
  607.     TQ3Vector3D                    normViewVector;
  608.     TQ3Vector3D                    eyeToFrontClip;
  609.     TQ3Vector3D                    eyeToBackClip;
  610.     float                        viewDistance;
  611.     TQ3Vector3D                    diagonalVector;
  612.     float                        ratio;
  613.     TQ3CameraObject                camera;
  614.     
  615.     TQ3ViewObject                theView = theDocument->fView ;
  616.     TQ3GroupObject                mainGroup = theDocument->fModel ;
  617.     
  618.     TQ3Point3D                    *documentGroupCenter = &theDocument->fGroupCenter ;
  619.     float                        *documentGroupScale  = &theDocument->fGroupScale ;
  620.  
  621.     Q3View_GetCamera( theView, &camera);
  622.     GetGroupBBox( theDocument, &viewBBox);
  623.  
  624.     /*
  625.      *  If we have a point model, then the "viewBBox" would end up
  626.      *  being a "singularity" at the location of the point.  As
  627.      *  this bounding "box" is used in setting up the camera spec,
  628.      *  we get bogus input into Escher.
  629.      */
  630.     xSize = viewBBox.max.x - viewBBox.min.x;
  631.     ySize = viewBBox.max.y - viewBBox.min.y;
  632.     zSize = viewBBox.max.z - viewBBox.min.z;
  633.  
  634.     if (xSize <= kQ3RealZero &&
  635.         ySize <= kQ3RealZero &&
  636.         zSize <= kQ3RealZero)  {
  637.         viewBBox.max.x += 0.0001;
  638.         viewBBox.max.y += 0.0001;
  639.         viewBBox.max.z += 0.0001;
  640.         
  641.         viewBBox.min.x -= 0.0001;
  642.         viewBBox.min.y -= 0.0001;
  643.         viewBBox.min.z -= 0.0001;
  644.     }
  645.  
  646.     points[0] = viewBBox.min;
  647.     points[1] = viewBBox.max;
  648.  
  649.     Q3Point3D_AffineComb(points, weights, 2, documentGroupCenter);
  650.  
  651.     /*
  652.      *  The "from" point is on a vector perpendicular to the plane
  653.      *  in which the bounding box has greatest dimension.  As "up" is
  654.      *  always in the positive y direction, look at x and z directions.
  655.      */
  656.     xSize = viewBBox.max.x - viewBBox.min.x;
  657.     zSize = viewBBox.max.z - viewBBox.min.z;
  658.     
  659.     if (xSize > zSize) {
  660.         fromAxis = kQ3AxisZ;
  661.     } else {
  662.         fromAxis = kQ3AxisX;
  663.     }
  664.  
  665.     /*
  666.      *  Compute the length of the diagonal of the bounding box.
  667.      *
  668.      *  The hither and yon planes are adjusted so that the
  669.       *  diagonal of the bounding box is 7/8 the size of the
  670.       *  minimum dimension of the view frustum. The diagonal is used instead
  671.       *  of the maximum size (in x, y, or z) so that when you rotate
  672.       *  the object, the corners don't get clipped out.
  673.       */
  674.     Q3Point3D_Subtract(
  675.         &viewBBox.max,
  676.         &viewBBox.min,
  677.         &diagonalVector);
  678.  
  679.     maxDimension    =    Q3Vector3D_Length(&diagonalVector);
  680.     maxDimension    *=    8.0 / 7.0;
  681.     
  682.     ratio = 1.0 / maxDimension;
  683.             
  684.     *documentGroupScale = ratio;
  685.     
  686.     Q3Camera_GetPlacement(camera, &placement);
  687.  
  688.     Q3Point3D_Subtract(
  689.         &placement.cameraLocation,
  690.         &placement.pointOfInterest,
  691.         &viewVector);
  692.         
  693.     viewDistance = Q3Vector3D_Length(&viewVector);
  694.     
  695.     Q3Vector3D_Normalize(&viewVector, &normViewVector);
  696.     
  697.     Q3Vector3D_Scale(&normViewVector, 
  698.                      viewDistance - ratio * maxDimension/2.0,
  699.                      &eyeToFrontClip);
  700.                     
  701.     Q3Vector3D_Scale(&normViewVector, 
  702.                     viewDistance + ratio * maxDimension/2.0,
  703.                     &eyeToBackClip);
  704.  
  705.     hither     = Q3Vector3D_Length(&eyeToFrontClip);
  706.     yon     = Q3Vector3D_Length(&eyeToBackClip);
  707.     
  708.     fieldOfView = 2 * atan((ratio * maxDimension/2.0)/hither);
  709.  
  710.     range.hither                 = hither;
  711.     range.yon                     = yon;
  712.  
  713.     Q3Camera_SetRange(camera, &range);
  714.  
  715.     Q3ViewAngleAspectCamera_SetFOV(
  716.         camera, fieldOfView);
  717.  
  718.     Q3ViewAngleAspectCamera_SetAspectRatio(
  719.         camera, (float) winWidth / (float) winHeight);
  720.  
  721.     Q3Object_Dispose(camera);
  722.     
  723.     return( *documentGroupCenter );
  724. }
  725.  
  726.